package com.itheima.reggie.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.reggie.common.R;
import com.itheima.reggie.dto.DishDto;
import com.itheima.reggie.entity.Category;
import com.itheima.reggie.entity.Dish;
import com.itheima.reggie.entity.DishFlavor;
import com.itheima.reggie.service.CategoryService;
import com.itheima.reggie.service.DishFlavorService;
import com.itheima.reggie.service.DishService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * @author ：石破天
 * @date ：Created in 2022年11月20日
 * @description ：
 * @version: 1.0
 */
@Slf4j
@RestController
@RequestMapping("/dish")
@Api(tags="菜品模块")
public class DishController {

    @Autowired
    private DishService dishService;

    @Autowired
    private CategoryService categoryService;

    @Autowired
    private DishFlavorService dishFlavorService;

    @Autowired
    private RedisTemplate redisTemplate;


    // 1: 添加菜品 修改菜品
    //  如果根据分类id查 redis有 去把redis找出来....去把集合转换出来 改吧改吧  再存进去
    // 2: 添加菜品 修改菜品
    //     根据 分类id删除....
    //    第二种删除 两种方式
    //        根据 分类id删除
     //        所有的 根据分类找菜品全部删除 ....  把所有键 dish_ 都删除的....
    //  增删改 缓存数据清空
    //  当你再查的时候  会 再查数据库


    @PostMapping
    public R<String> save(@RequestBody DishDto dishDto){
        // dishDto 既有要保存的 dish内容 又有要保存 dishflavor

        // 自己声明一个 保存菜品的同时保存口味信息
        dishService.saveDishAndFlavor(dishDto);


          //  keys  dish_*  找到所有相关键
        Set keys = redisTemplate.keys("dish_*");//
        redisTemplate.delete(keys);
        //基于 dishDto 完成dish数据的保存 和 dishFlavor数据的保存
        return R.success("菜品及菜品口味信息保存成功");

    }
    /**
     * 基础分页查询 带条件
     */
    @GetMapping("/page")
    @ApiOperation("菜品分页查询接口")
    @ApiImplicitParams(
            {
                    @ApiImplicitParam(name="page",value = "页码",required = true),
                    @ApiImplicitParam(name="pageSize",value = "每页记录数",required = true),
                    @ApiImplicitParam(name="name",value = "菜品名称",required = false),
            }
    )
    public R<Page> page(int page,int pageSize,String name){
        // 根据 name条件 模糊查询 并且完成 分页展示
        // 1:构建 分页构造器
        Page<Dish> pageInfo = new Page<Dish>(page,pageSize);


        // 2:构建 条件构造器
        LambdaQueryWrapper<Dish> wrapper = new LambdaQueryWrapper<>();
        // 3: 添加条件
        wrapper.like(StringUtils.isNotEmpty(name),Dish::getName,name);
        // 隐形条件  有排序字段 安装排序字段排  正序
        //     没有排序字段 按照更新时间排  逆序
        wrapper.orderByDesc(Dish::getUpdateTime);

        // 4: 执行方法
        dishService.page(pageInfo,wrapper);

        // pageInfo  getTotal() 总记录数
        //           getRecords()---List<Dish> 当前页的dish信息

        // 问题在于我们返回的 Pgae<Dish>  pageInfo
                  //       List<Dish>  records
                 //            Dish  Dish里面没有categoryName属性

        // Dish里面能够返回 categoryName? 能够有这个属性?
         //  Dish里面有没有这个属性? 没有  dish表和Dish类对应.
        //    你的儿子有  DishDto有  DishDto是Dish跟前端交互的补充.
        //  因为这个属性 categoryName 是跟前端交互的属性 不是数据库的属性


        // 那你就不能 返回给页面Pgae<Dish>了?
         //  得返回 Page<DishDto>
        Page<DishDto> dishDtoPage = new Page<>();
        // pageInfo 里面有数据库查询到的分页的数据
        // pageInfo的属性赋值给 dishDtoPage
       // BeanUtils.copyProperties(pageInfo,dishDtoPage);//反射 泛型擦除了...
       //直接这么写  dishDtoPage里面的列表 相当于变成 List<Dish>
        BeanUtils.copyProperties(pageInfo,dishDtoPage,"records");
       //  这次这么写  dishDtoPage 除了 records 其他属性都有值了!!!
        // 只需把  设计一个集合  List<DishDto>
                         // 里面存放 多个DishDto对象
        List<DishDto> records = new ArrayList<>();
        // 中间就需要咱们 把 records填满.....
       // 数据来源   pageInfo的 List<Dish> records中
        List<Dish> dishList = pageInfo.getRecords();
        // 关键就是把  List<Dish> dishList ==> List<DishDto> records
        // 遍历有数据的  填充没数据的
        for (Dish dish : dishList) {
            // dish里面有数据
            // 把dish里面数据取出来.... 放到一个 dishDto中 dishDto存 records中
            DishDto dishDto = new DishDto();
            // 想把 dish里面所有属性 复制 给 dishDto中
            BeanUtils.copyProperties(dish,dishDto);
            // dish把它能给 dishDto的属性 都给了...
            System.out.println(dishDto);

            //dishDto.setCategoryName("鲁菜");//先写死
            //先获取 到  categort_id  根据id查到分类信息 获取分类名称
            Long categoryId = dish.getCategoryId();
            //  需要 查询category表  需要依赖 categoryService
            Category category = categoryService.getById(categoryId);
            dishDto.setCategoryName(category.getName());
            // dishDto要存到哪里?
            records.add(dishDto);
        }

        dishDtoPage.setRecords(records);
        // 5: 把page对象设置给R 返回
        return R.success(dishDtoPage);



    }


    /**
     *  根据id 查询 dishDto数据
     */
    @GetMapping("/{id}")
    public R<DishDto> getById(@PathVariable Long id){
        // DishDto对象能从数据库直接查询出来吗?

        // 需要我们去设置 去组织.... 去查询两张表 两种表的数据 组织到 DishDto中
        DishDto dishDto = dishService.getByIdWithFlavor(id);
                          //   这个方法需要定制  alt+enter

        return R.success(dishDto);
    }

    /**
     * 根据菜品id 更新菜品及口味最新数据
     *     逻辑
     *        根据菜品id更新 菜品数据
     *        根据菜品id 删除以前的口味数据
     *        根据菜品id 添加新的口味数据
     */
    @PutMapping
    public R<String> update(@RequestBody DishDto dishDto){
        log.info("查看更新后的数据:"+dishDto);
        log.info("更新后的dishDto:{}",dishDto);

        // 既要更新菜品有要更新口味...
        dishService.updateWithFlavor(dishDto);
        // 因为这次业务是更新菜品和口味信息  service单表操作
        // 所以需要自定义方法
        //  keys  dish_*  找到所有相关键
        Set keys = redisTemplate.keys("dish_*");//
        redisTemplate.delete(keys);
        return R.success("更新菜品和口味信息成功");
    }
    /**
     * 根据ids们  进行批量的启售和停售
     *      *            包含单个启售停售
     * @param sta  要修改的状态 在路径上 路径参数形式接收
     * @param ids  哪些id对应的菜品要 修改状态
     * @return
     */
    @PostMapping("/status/{sta}")//sta是接收改变状态的
    public R<String> updateStatusByIds(@PathVariable Integer sta,Long[] ids){
        // 批量修改
        //  // 根据ID 批量更新
        //boolean updateBatchById(Collection<T> entityList);
        // update dish set status=#{sta} where id in []

        // update dish set status=#{sta} where id = ?
      // // 根据 UpdateWrapper 条件，更新记录 需要设置sqlset
        //boolean update(Wrapper<T> updateWrapper);
        // updateWrapper 是 queryWrapper升级
        //  根据条件更新
        for (Long id : ids) {
            // update dish set status=#{sta} where id = ?  每次都是新的
            LambdaUpdateWrapper<Dish> updateWrapper = new LambdaUpdateWrapper<>();
            //  status 是 更新条件
            updateWrapper.set(Dish::getStatus,sta);// set status=#{sta}
            // id  where 条件拼接
            updateWrapper.eq(Dish::getId,id);//where id=#{id}

            dishService.update(updateWrapper);
        }

        //  keys  dish_*  找到所有相关键
        Set keys = redisTemplate.keys("dish_*");//
        redisTemplate.delete(keys);

        return R.success("批量更新状态成功");
    }


//
//    @GetMapping("/list")
//    public R<List<Dish>> list(Long categoryId){
//
//    }
    /**
     * 根据分类id 查询该分类下指定的 菜品信息
     *    dish类型可以接收
     *       categoryId   因为有categoryId属性
     *       可以接收我dish其他属性作为条件
     *       提高方法的复用性-----高内聚 我一个人顶多个人
     */
    @GetMapping("/list")
    public R<List<DishDto>> list(Dish dish){
         log.info("根据id查询菜品:"+dish.getCategoryId());


         // List<DishDto>  这个代表着给前端页面的数据 ...
        /*
           从 redis中 取出来 该分类下对应的  redisDtoList 数据.
           想取出来 要根据键 去取
             键  保证位置
             键    直接用分类id 作为键?   categoryId

                  理论上可以但是,实际容易有冲突
                      dish_categoryId
                   dish_1338378363876
                   dish_1389337337388
             值
                redisDtoList    类型你就用值String

                redisDtoList 存到redis
                 底层 redisDtoList(Object)  按照一种字符串的编码方式,编成字节文件--存到redis中   序列化
                 底层 redis找到对应字节--- 按照之前的编码方式进行解码--(Object)  反序列化
                 要求 存到redis数据必须实现序列化....

         */
        // 先取出来
        List<DishDto> redisDtoList = null;
        // 从redis读取该分类下的 菜品信息
        String key = "dish_"+dish.getCategoryId();// dish_1338378363876
        redisDtoList = (List<DishDto>)redisTemplate.opsForValue().get(key);
                     // 取出之后是Object 强转
        log.info("我们从redis取出的集合数据:"+redisDtoList);
        if(redisDtoList!=null){// 说明有

            log.info(dish.getCategoryId()+"从缓存中获取到该分类id下的菜品数据了,无需查询数据库");
            return R.success(redisDtoList);//有就不用再查数据库 直接返回
        }
      // 下面代表 redis没有还是要数据库查询

        // select *from dish where category_id = ?
        LambdaQueryWrapper<Dish> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(dish.getCategoryId()!=null,Dish::getCategoryId,dish.getCategoryId());
                   // 判断是不是获取到了categoryId       =前面  "categoy_id"   传递过来的值
        // 隐含了一个条件
        wrapper.eq(Dish::getStatus,1);//启售的
        // 查询
        List<Dish> list = dishService.list(wrapper);

        // 构建一个 List<DishDto>
        // 把 List<Dish>  每一个Dish 都变成 DishDto 存到 集合中
        List<DishDto> dishDtoList = new ArrayList<>();
        // 遍历 list
        if(list!=null&&list.size()>0){//有内容
            for (Dish dish1 : list) {
              DishDto dishDto = new DishDto();
              BeanUtils.copyProperties(dish1,dishDto);
              // 还得根据 id查询 菜品口味信息
                // 成员位置引入 菜品口味service
                // select *from dish_flavor where dish_id=...
                LambdaQueryWrapper<DishFlavor> wrapper1 = new LambdaQueryWrapper<>();
                wrapper1.eq(DishFlavor::getDishId,dish1.getId());
                             //  where dish_id=#{..}
                List<DishFlavor> dishFlavors = dishFlavorService.list(wrapper1);
                // 集合 添加到 dishDto
                dishDto.setFlavors(dishFlavors);
                //再把 dishDto存到 返回的集合中
                dishDtoList.add(dishDto);
            }
        }

        // 是不是已经查到了  往 redis存一份  dishDtoList查询到组合的数据
        redisTemplate.opsForValue().set(key,dishDtoList);

        return R.success(dishDtoList);
    }
}
